
--{{GLOBAL}}--
STORE_CREDIT = "default:grass_1"

-- reports the contents of a list
-- channel intended for LCDs
local function showlist(channel, list, empty_msg)
    empty_msg = empty_msg or "Empty List."
    local size = table.maxn(list)
    if size <= 0 then
        digiline_send(channel, empty_msg)
        return
    end
    local longstr= ""
    for _, v in ipairs(list) do
        longstr = longstr .. v.name .. ", ".. v.count .."; \n"
    end
    digiline_send(channel, longstr)
end

local function is_in_list(string, list)
    for i, v in ipairs(list) do
        if v.name == string then
            return i;
        end
    end
    return -1
end

local function additem(list, stack)
    local index = is_in_list(stack.name, list)
-- if the item is not in the list or the list is empty
    if (index < 0 or table.maxn(list) <= 0) and stack.count > 0 then
        table.insert(list, stack)
        return
    elseif index > 0 then
-- if the item is in the list and it must be updated
        list[index].count = list[index].count + stack.count
        if list[index].count <= 0 then
            table.remove(list, index)
        end
    end

end

-- use for both payment chest and stock chest
-- payment chest responds to uput or utake
-- stock chest responds to tput or ttake
local function detect(msg, list)
    if msg  == nil then 
        return 
    end
    if msg.stack == nil then 
        return 
    end
    local n = msg.stack.name
    local c = msg.stack.count
    if msg.action == "uput" then
    -- player has put an item stack in the digichest
        additem(list, {name=n, count= c})
    elseif msg.action == "utake" then
    -- player has taken an item
        additem(list, {name=n, count= -c})
    elseif msg.action  == "tput" then
    -- a tube has put an item
        additem(list, {name=n, count= c})
--        showlist("lcd", mem.paid)
    elseif msg.action == "ttake" then
    -- a tube has taken an item
        additem(list, {name=n, count= -c})
    end
end

-- only take specified item as currency
-- to either load funds or refund
local function process_payment(dfi, list, currency)
    local size = table.maxn(list)
    if size <= 0 then
        return false
    end
    for i, v in ipairs(list) do
        if v.name == currency then
            digiline_send(dfi, v)
            return true
        end
    end
end

local function process_sale(dfi, order, itemname)
    if itemname == nil then
        return "Programming error: var 'itemname' is nil"
    end
    if tonumber(order[itemname]) == nil then 
        return "Error: invalid input: not a number."
    end
    local quantity = math.floor(order[itemname])
    if quantity <= 0 then
        return "Error: invalid quantity (".. quantity ..")"
    end
    for i, v in ipairs(mem.stock) do
        if v.name == itemname and quantity > v.count then
            return "Order quantity exceeds stock!"
        end
    end
    local cost = 1 * quantity
    if table.maxn(mem.paid) <= 0 then
    -- note: tried to check mem.paid == {} - doesn't work
        return "Error: no funds loaded."
    end
    if mem.paid[1].count < cost then
        return "Error: Insufficient funds."
    end
    -- sale is good to go
    digiline_send(dfi, {name=itemname, count=quantity})
    additem(mem.paid, {name=mem.paid[1].name, count=-cost})
    return "Sale confirmed"
end

-- dfi_receive = customer payment
-- dfi_send = send to customer (refunds, wares)
local function touch_response(msg, dfi_receive, dfi_send)
-- ignore the event when a player closes menu
    local lcd_status = "lcd3"
    local credit = STORE_CREDIT
    if msg["key_enter_field"] == nil and msg["quit"] ~= nil then 
        return "Hello! Use the touchscreen to begin."
    end
    if msg["pay"] ~= nil then
        if process_payment(dfi_receive, mem.queue, credit) then
            return "Sent payment."
        else
            return "No payment items detected."
        end
    elseif msg["refund"] ~= nil then
        if process_payment(dfi_send, mem.paid, credit) then
            return "Sent refund."
        else
            return "No payment to return."
        end
    end
    for i, itemstack in ipairs(mem.stock) do
        if msg["key_enter_field"] == itemstack.name then
            return process_sale(dfi_send, msg, itemstack.name)
        end
    end
end

local function get_texture_from_itemname(itemname) 
    -- no access to :gsub()
    local newChar = "_"
    local skip = 0
    -- mods with texture files that do not use prefixes
    if string.sub(itemname, 1,5) == "nssm:" then --can't access find() ?'
        skip = 5
--     elseif string.sub(itemname,1,7) == "bucket:" then
--         skip = 7
    elseif itemname == "bucket:bucket_empty" then
        return "bucket.png"
    end

    local charTable = {}
    for i=1, #itemname do
        if i > skip then
            local foo = string.sub(itemname, i, i)
            if foo ~= ":" then
                table.insert(charTable,foo)
            else
                table.insert(charTable,"_")
            end
        end
    end
    return table.concat(charTable) .. ".png"
end

local function spawnfield(channel, name, label, default, y)
    local filename = get_texture_from_itemname(name) 

    local height = 1
    local width = 7
    local n = {}
    n.command = "addfield"
    n["X"] = 2
    n["Y"] = 1 + y
    n["W"] = width
    n["H"] = height
    n["name"] = name
    n["label"] = label
    n["default"] = default
    digiline_send(channel, n)

    local icon = {}
    icon.command = "addimage"
    icon["X"] = 0.5
    icon["Y"] = 0.5 + y
    icon["W"] = 1
    icon["H"] = 1
    icon.texture_name = filename
    digiline_send(channel,icon)
end

local function touch_init(channel, wares)
    local reset = {}
    reset.command = "clear"
    digiline_send(channel, reset)
    local y_offset = 0


    local btn_pay = {}
    btn_pay.command = "addbutton"
    btn_pay["X"] = 1
    btn_pay["Y"] = y_offset
    btn_pay["W"] = 4
    btn_pay["H"] = 1
    btn_pay["name"] = "pay"
    btn_pay["label"] = "Deposit"
    digiline_send(channel, btn_pay)

    local btn_refund = {}
    btn_refund.command = "addbutton"
    btn_refund["X"] = 5
    btn_refund["Y"] = y_offset
    btn_refund["W"] = 4
    btn_refund["H"] = 1
    btn_refund["name"] = "refund"
    btn_refund["label"] = "Refund"
    digiline_send(channel, btn_refund)
    y_offset = y_offset + 1

    local guide = {}
    guide.command = "addlabel"
    guide["X"] = 2.5
    guide["Y"] = y_offset
    guide.label = "Press ENTER (or RETURN) key to place order."
    digiline_send(channel, guide)
    y_offset = y_offset + 0.5

    local price = {}
    price.command = "addlabel"
    price["X"] = 2.5
    price["Y"] = y_offset
    price.label = "Items cost 1x Grass (default:grass_1) each."
    digiline_send(channel, price)
    -- let for loop increment y
    y_offset = y_offset - 0.5 -- (0.5 - 1 = -0.5)

    for i, v in ipairs(wares) do
        local label = v.name .. " (stock: " .. v.count .. ")"
        spawnfield(channel, v.name, label, "0", y_offset + i)
    end
end

local function main()
    local ts    = "ts"      -- touchscreen, order form
    local pay   = "input"   -- player pays w/ digichest
    local dfi1  = "accept"  -- DFI sends payment from 'pay'
    local store = "store"   -- digichest holds stock & payment
    local dfi2  = "deploy"  -- DFI sends wares from stock
    local card  = "swipe"   -- experimental card reader
    local lcd_queue = "lcd"
    local lcd_paid  = "lcd2"
    if event.type == "program" then
        mem.queue = {} -- items placed in chest
        mem.paid = {}  -- items paid by customers
        mem.stock = {} -- items stocked by owner
        touch_init(ts, mem.stock)
        digiline_send("lcd", "Initialized!")
        digiline_send("lcd2", "Initialized!")
        digiline_send("lcd3", "Initialized!")
    end
    if event.type == "digiline" and event.channel == ts then
        local status = touch_response(event.msg, dfi1, dfi2)
        touch_init(ts, mem.stock)
        digiline_send("lcd3", status)
        showlist(lcd_paid, mem.paid, "No funds loaded.")
    end
    if event.type == "digiline" and event.channel == pay then
        detect(event.msg, mem.queue)
        showlist(lcd_queue, mem.queue, "Place payment in the chest below.")
    end
    if event.type == "digiline" and event.channel == store then
        if event.msg.action == "uput" 
        or event.msg.action == "utake" then
        -- store stock directly accessed by player 
        -- (as opposed to vending)
            detect(event.msg, mem.stock)
        elseif event.msg.action == "tput" then
        -- vending, payment confirmation
        -- update mem.paid to show current store credits
            detect(event.msg, mem.paid)
            showlist(lcd_paid, mem.paid, "No funds loaded.")
        elseif event.msg.action == "ttake" then
        -- vending, update stock after selling
            if event.msg.stack.name == STORE_CREDIT then
                detect(event.msg, mem.paid)
            else
                detect(event.msg, mem.stock)
            end
            showlist(lcd_paid, mem.paid, "No funds loaded.")
        -- process_sale already deducts store creditss
        -- note: upon refunds, the code will attempt to deduct
        -- currency from the stock list
        -- however additems() does allow negative quantities to be indexed.
        end
        touch_init(ts, mem.stock)
    end
end

return main()
